﻿using SharpDepend.Datatypes;
using System;
using System.Collections.Generic;
using System.Linq;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.GUI.Controls;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Modules.Container;
using ZquenceStudio3Host.Commands.BuiltInCommands;
using ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.GUI;

namespace ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine.Commands
{
    class MakeGroupCommand : BaseCommand
    {
        private NodisizerAreaHandler mNodisizerArea;
        private BaseModulizerUI mNodisizer;
        private List<BaseModule> mModules;
        private BaseModule mParent;
        private GroupModule mCreatedGroupModule;
        private ModulizerUIv2 mModisizerV2;

        public override object[] ReferenceObjects
        {
            get
            {
                return RefereceObject(mParent);
            }
        }

        public MakeGroupCommand(NodisizerAreaHandler nodisizerArea, BaseModulizerUI nodisizer, IEnumerable<BaseModule> modulePanels)
        {
            mNodisizerArea = nodisizerArea;
            mNodisizer = nodisizer;

            Init(modulePanels);
        }

        public MakeGroupCommand(ModulizerUIv2 nodisizer, IEnumerable<BaseModule> modulePanels)
        {
            mModisizerV2 = nodisizer;

            Init(modulePanels);
        }

        private void Init(IEnumerable<BaseModule> modulePanels)
        {
            mParent = null;

            if (modulePanels == null)
            {
                throw new ArgumentNullException(nameof(modulePanels));
            }
            if (modulePanels.Count() < 2)
            {
                throw new Exception("A module group must have at least 2 modules.");
            }
            foreach (var modulePanel in modulePanels)
            {
                if (mParent == null)
                {
                    mParent = modulePanel.ParentModule;
                }
                else
                {
                    if (mParent != modulePanel.ParentModule)
                    {
                        throw new Exception("Modules that will be added to a group must contains in the same module parent.");
                    }
                }
            }
            if (mParent == null)
            {
                throw new Exception("Modules do not includes in any parent module.");
            }

            mModules = new List<BaseModule>(modulePanels);
        }

        protected override void Execute()
        {
            // Create group module.
            if (mCreatedGroupModule == null)
            {
                mCreatedGroupModule = new GroupModule(mModisizerV2 != null ? mModisizerV2.mModuleManager : mNodisizer.Modulizer.mModuleManager);
                mCreatedGroupModule.ParentModule = mParent;
            }

            Vector4 averangePosition = Vector4.Zero;

            // Add all modules to the group and calculate the averange position for group.
            foreach (var module in mModules)
            {
                // If this modules is top modules, tell the front end that this will be removed.
                if (module.IsTopModule())
                {
                    mModisizerV2.FrontEnd.RemoveModule(module, false);
                    mModisizerV2.FrontEnd.AddModule(module, true);
                }

                // Exclude from parent.
                (mParent as GroupModule).ExcludeSubModule(module);

                // Include to group.
                mCreatedGroupModule.IncludeSubModule(module);
                
                averangePosition += module.GuiPosition;
            }
            averangePosition /= mModules.Count();
            mCreatedGroupModule.GuiPosition = averangePosition;
            
            // Add group module to parent.
            (mParent as GroupModule).AddSubModule(mCreatedGroupModule);

            if (mModisizerV2 != null)
            {
                mModisizerV2.GoInto(mModules[0].ParentModule, true);
            }
            else
            {
                mNodisizer.RebuildSynthGraphModuleParent(mModules[0]);
            }
        }

        protected override void UnExecute()
        {
            // Remove group module to parent.
            (mParent as GroupModule).RemoveSubModule(mCreatedGroupModule);
            
            foreach (var module in mModules)
            {
                // Exclude from group.
                mCreatedGroupModule.ExcludeSubModule(module);

                // Include to parent.
                (mParent as GroupModule).IncludeSubModule(module);

                // If parent is root module, tell the front end that those new modules will be includes.
                if (mParent.IsRootModule())
                {
                    mModisizerV2.FrontEnd.RemoveModule(module, true);
                    mModisizerV2.FrontEnd.AddModule(module, false);
                    mModisizerV2.UpdateModuleUIUp(module);
                }
            }

            if (mModisizerV2 != null)
            {
                mModisizerV2.TrackArea.Editor.SelectorHandler.ClearSelection();
                mModisizerV2.GoInto(mModules[0].ParentModule, true);
            }
            else
            {
                mNodisizerArea.Editor.SelectorHandler.ClearSelection();
                mNodisizer.RebuildSynthGraphModuleParent(mModules[0]);
            }
        }

        public override string ToString()
        {
            return "Make group";
        }
    }
}
